package com.chujian.framework.sync;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapBuilder;
import com.chujian.framework.constant.ThreadConst;
import com.chujian.framework.sync.handler.ISyncMethodHandler;
import com.chujian.framework.sync.sender.ISyncDataSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 同步数据调度
 */
@Slf4j
public class SyncDataDispatcher implements InitializingBean {

    @Autowired
    private List<ISyncMethodHandler> handlerList;
    @Autowired(required = false)
    private List<SyncDataWrapper> dataWrappers;
    @Autowired(required = false)
    private List<ISyncDataSender> syncDataSenders;

    // method -> ISyncMethodHandler
    private final Map<String, ISyncMethodHandler> handlerMap = new HashMap<>();

    /**
     * 调度需要同步的数据
     * 在异步线程中执行。不影响正常业务
     *
     * @param target      拦截到的对象，一般是service
     * @param method      拦截到的方法
     * @param args        调用方法的参数
     * @param returnValue 方法返回值
     */
    @Async(ThreadConst.CORE_EXECUTOR)
    public void dispatch(Object target, Method method, Object[] args, Object returnValue) {
        String methodName = method.getName();

        // 1. 获取方法处理器
        ISyncMethodHandler handler = handlerMap.get(methodName);

        if (null == handler) {
            log.error("there is no sync handler for method [{}], please dont use this method to change data", methodName);
            return;
        }

        // 2. 生成同步对象
        SyncData syncData = handler.creSyncData(method, args, returnValue, target);
        if (null == syncData) {
            return;
        }

        // 3. 同步对象包装
        syncData = wrappersSyncData(syncData);

        log.debug("the data to sync：{}", syncData);

        // 4. 调用发送
        send(syncData);
    }

    /**
     * 包装需要同步的数据
     *
     * @param syncData
     * @return
     */
    private SyncData wrappersSyncData(SyncData syncData) {
        if (CollUtil.isNotEmpty(dataWrappers)) {
            for (SyncDataWrapper wrapper : dataWrappers) {
                if (wrapper.support(syncData)) {
                    syncData = wrapper.wrapper(syncData);
                }
            }
        }
        return syncData;
    }

    /**
     * 调用发送
     *
     * @param syncData syncData
     */
    private void send(SyncData syncData) {
        if (CollUtil.isEmpty(syncDataSenders)) {
            log.error("please provide a bean of ISyncDataSender");
            return;
        }
        Map<String, Object> headers = MapBuilder.<String, Object>create()
                .put(SyncConst.HEADER_CHANGE_TYPE, syncData.getChangeType())
                .put(SyncConst.HEADER_DATA_ID, syncData.getDataId())
                .put(SyncConst.HEADER_DATA_MARK, syncData.getDataMark())
                .build();
        for (ISyncDataSender syncDataSender : syncDataSenders) {
            try {
                syncDataSender.send(headers, syncData);
            } catch (Exception e) {
                log.error("数据同步发送失败", e);
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        for (ISyncMethodHandler handler : handlerList) {
            handlerMap.put(handler.supportMethod(), handler);
        }
    }
}
